]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
keyboard: Refactor 'commit' action handling
authorCarlos Garnacho <carlosg@gnome.org>
Tue, 30 Jan 2024 10:08:52 +0000 (11:08 +0100)
committerCarlos Garnacho <carlosg@gnome.org>
Thu, 29 Feb 2024 15:42:22 +0000 (16:42 +0100)
Move the complex parts of this mechanism from the Keyboard object
to the KeyboardController object, replaced by code that is easier to
follow.

Also, keyval guessing is deferred to a later point in the commit
procedure so so it does not happen for a single character only,
this way we can send multi-character input through the IM, which
is necessary for some OSK layouts.

Closes: https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/7190
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/3162>

js/ui/keyboard.js

index 5ebe3f56a4c7f8c5f4dc26e14f67c2fdd4f47ed9..661c88038c69064c97108891f28eb6fe1e29ef0e 100644 (file)
@@ -224,7 +224,7 @@ const Key = GObject.registerClass({
         'pressed': {},
         'released': {},
         'keyval': {param_types: [GObject.TYPE_UINT]},
-        'commit': {param_types: [GObject.TYPE_UINT, GObject.TYPE_STRING]},
+        'commit': {param_types: [GObject.TYPE_STRING]},
     },
 }, class Key extends St.BoxLayout {
     _init(params, extendedKeys = []) {
@@ -281,11 +281,6 @@ const Key = GObject.registerClass({
         this.keyButton._extendedKeys = this._extendedKeyboard;
     }
 
-    _getKeyvalFromString(string) {
-        let unicode = string?.length ? string.charCodeAt(0) : undefined;
-        return Clutter.unicode_to_keysym(unicode);
-    }
-
     _press(button) {
         if (button === this.keyButton) {
             this._pressTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
@@ -320,12 +315,10 @@ const Key = GObject.registerClass({
         if (this._pressed) {
             if (this._keyval && button === this.keyButton)
                 this.emit('keyval', this._keyval);
-            else if (commitString) {
-                const keyval = this._getKeyvalFromString(commitString);
-                this.emit('commit', keyval, commitString);
-            } else {
+            else if (commitString)
+                this.emit('commit', commitString);
+            else
                 console.error('Need keyval or commitString');
-            }
         }
 
         this.emit('released');
@@ -805,7 +798,7 @@ const EmojiPager = GObject.registerClass({
             key.connect('pressed', () => {
                 this._currentKey = key;
             });
-            key.connect('commit', (actor, keyval, str) => {
+            key.connect('commit', (actor, str) => {
                 if (this._currentKey !== key)
                     return;
                 this._currentKey = null;
@@ -1266,6 +1259,7 @@ export const Keyboard = GObject.registerClass({
 
         this._clearShowIdle();
 
+        this._keyboardController.oskCompletion = false;
         this._keyboardController.destroy();
 
         Main.layoutManager.untrackChrome(this);
@@ -1276,8 +1270,6 @@ export const Keyboard = GObject.registerClass({
             this._languagePopup.destroy();
             this._languagePopup = null;
         }
-
-        IBusManager.getIBusManager().setCompletionEnabled(false, () => Main.inputMethod.update());
     }
 
     _setupKeyboard() {
@@ -1301,7 +1293,7 @@ export const Keyboard = GObject.registerClass({
         this._emojiSelection.connect('toggle', this._toggleEmoji.bind(this));
         this._emojiSelection.connect('close-request', () => this.close(true));
         this._emojiSelection.connect('emoji-selected', (selection, emoji) => {
-            this._keyboardController.commitString(emoji);
+            this._keyboardController.commit(emoji).catch(console.error);
         });
 
         this._emojiSelection.hide();
@@ -1447,12 +1439,13 @@ export const Keyboard = GObject.registerClass({
             }
 
             if (key.action !== 'modifier') {
-                button.connect('commit', (_actor, keyval, str) => {
-                    this._commitAction(keyval, str).then(() => {
+                button.connect('commit', (_actor, str) => {
+                    this._keyboardController.commit(str, this._modifiers).then(() => {
+                        this._disableAllModifiers();
                         if (layout.mode === 'default' ||
                             (layout.mode === 'latched' && !this._latched))
                             this._setActiveLevel('default');
-                    });
+                    }).catch(console.error);
                 });
             }
 
@@ -1510,30 +1503,6 @@ export const Keyboard = GObject.registerClass({
         }
     }
 
-    async _commitAction(keyval, str) {
-        if (this._modifiers.size === 0 && str !== '' &&
-            keyval && this._oskCompletionEnabled) {
-            if (await Main.inputMethod.handleVirtualKey(keyval))
-                return;
-        }
-
-        if (str === '' || !Main.inputMethod.currentFocus ||
-            (keyval && this._oskCompletionEnabled) ||
-            this._modifiers.size > 0 ||
-            !this._keyboardController.commitString(str, true)) {
-            if (keyval !== 0) {
-                this._forwardModifiers(this._modifiers, Clutter.EventType.KEY_PRESS);
-                this._keyboardController.keyvalPress(keyval);
-                GLib.timeout_add(GLib.PRIORITY_DEFAULT, KEY_RELEASE_TIMEOUT, () => {
-                    this._keyboardController.keyvalRelease(keyval);
-                    this._forwardModifiers(this._modifiers, Clutter.EventType.KEY_RELEASE);
-                    this._disableAllModifiers();
-                    return GLib.SOURCE_REMOVE;
-                });
-            }
-        }
-    }
-
     _setLatched(latched) {
         this._latched = latched;
         this._setCurrentLevelLatched(this._currentPage, this._latched);
@@ -1554,15 +1523,6 @@ export const Keyboard = GObject.registerClass({
         this._setModifierEnabled(keyval, !isActive);
     }
 
-    _forwardModifiers(modifiers, type) {
-        for (const keyval of modifiers) {
-            if (type === Clutter.EventType.KEY_PRESS)
-                this._keyboardController.keyvalPress(keyval);
-            else if (type === Clutter.EventType.KEY_RELEASE)
-                this._keyboardController.keyvalRelease(keyval);
-        }
-    }
-
     _disableAllModifiers() {
         for (const keyval of this._modifiers)
             this._setModifierEnabled(keyval, false);
@@ -1699,8 +1659,7 @@ export const Keyboard = GObject.registerClass({
             return;
         }
 
-        this._oskCompletionEnabled =
-            IBusManager.getIBusManager().setCompletionEnabled(true, () => Main.inputMethod.update());
+        this._keyboardController.oskCompletion = true;
         this._clearKeyboardRestTimer();
 
         if (immediate) {
@@ -1735,8 +1694,7 @@ export const Keyboard = GObject.registerClass({
         if (!this._keyboardVisible)
             return;
 
-        IBusManager.getIBusManager().setCompletionEnabled(false, () => Main.inputMethod.update());
-        this._oskCompletionEnabled = false;
+        this._keyboardController.oskCompletion = false;
         this._clearKeyboardRestTimer();
 
         if (immediate) {
@@ -2014,15 +1972,72 @@ class KeyboardController extends Signals.EventEmitter {
         return this._currentSource.xkbId;
     }
 
-    commitString(string, fromKey) {
-        if (string == null)
-            return false;
-        /* Let ibus methods fall through keyval emission */
-        if (fromKey && this._currentSource.type === InputSourceManager.INPUT_SOURCE_TYPE_IBUS)
-            return false;
+    _forwardModifiers(modifiers, type) {
+        for (const keyval of modifiers) {
+            if (type === Clutter.EventType.KEY_PRESS)
+                this.keyvalPress(keyval);
+            else if (type === Clutter.EventType.KEY_RELEASE)
+                this.keyvalRelease(keyval);
+        }
+    }
+
+    _getKeyvalsFromString(string) {
+        const keyvals = [];
+        for (const unicode of string) {
+            const keyval = Clutter.unicode_to_keysym(unicode.codePointAt(0));
+            // If the unicode character is unknown, try to avoid keyvals at all
+            if (keyval === (unicode || 0x01000000))
+                return [];
+
+            keyvals.push(keyval);
+        }
+
+        return keyvals;
+    }
+
+    async commit(str, modifiers) {
+        const keyvals = this._getKeyvalsFromString(str);
+
+        // If there is no IM focus (e.g. with X11 clients), or modifiers
+        // are in use, send raw key events.
+        if (!Main.inputMethod.currentFocus || modifiers?.size > 0) {
+            if (modifiers)
+                this._forwardModifiers(modifiers, Clutter.EventType.KEY_PRESS);
+
+            for (const keyval of keyvals) {
+                this.keyvalPress(keyval);
+                this.keyvalRelease(keyval);
+            }
+
+            if (modifiers)
+                this._forwardModifiers(modifiers, Clutter.EventType.KEY_RELEASE);
 
-        Main.inputMethod.commit(string);
-        return true;
+            return;
+        }
+
+        // If OSK completion is enabled, or there is an active source requiring
+        // IBus to receive input, prefer to feed the events directly to the IM
+        if (this._oskCompletionEnabled ||
+            this._currentSource.type === InputSourceManager.INPUT_SOURCE_TYPE_IBUS) {
+            for (const keyval of keyvals) {
+                // eslint-disable-next-line no-await-in-loop
+                if (!await Main.inputMethod.handleVirtualKey(keyval)) {
+                    this.keyvalPress(keyval);
+                    this.keyvalRelease(keyval);
+                }
+            }
+            return;
+        }
+
+        Main.inputMethod.commit(str);
+    }
+
+    set oskCompletion(enabled) {
+        if (this._oskCompletionEnabled === enabled)
+            return;
+
+        this._oskCompletionEnabled =
+            IBusManager.getIBusManager().setCompletionEnabled(enabled, () => Main.inputMethod.update());
     }
 
     keyvalPress(keyval) {